package com.like.banzipao.server.util

import com.like.banzipao.server.model.Poker

/**
 * 出牌规则
 */
object PlayRuleUtils {
    /**
     * 判断出牌是否有效
     */
    fun isValid(prePlayPokers: List<Poker>, curPlayPokers: List<Poker>): Boolean {
        val curPlayPokerType = try {
            getPlayType(curPlayPokers)
        } catch (e: Exception) {
            return false
        }
        if (prePlayPokers.isEmpty()) {
            return true
        }
        if (curPlayPokerType == PlayPokerType.TIAN_BAO) {
            return true
        }
        return when (getPlayType(prePlayPokers)) {
            PlayPokerType.DAN_ZHANG -> {
                when {
                    curPlayPokerType == PlayPokerType.DAN_ZHANG -> curPlayPokers.first().getValue() > prePlayPokers.first().getValue()
                    curPlayPokerType.isBoom() -> true
                    else -> false
                }
            }

            PlayPokerType.DUI_ZI -> {
                when {
                    curPlayPokerType == PlayPokerType.DUI_ZI -> curPlayPokers.first().getValue() > prePlayPokers.first().getValue()
                    curPlayPokerType.isBoom() -> true
                    else -> false
                }
            }

            PlayPokerType.LIAN_ZI -> {
                when {
                    curPlayPokerType == PlayPokerType.LIAN_ZI -> curPlayPokers.size == prePlayPokers.size && curPlayPokers.first()
                        .getValue() > prePlayPokers.first().getValue()

                    curPlayPokerType.isBoom() -> true
                    else -> false
                }
            }

            PlayPokerType.ZHA_DAN_SAN_ZHANG -> {
                when (curPlayPokerType) {
                    PlayPokerType.ZHA_DAN_SAN_ZHANG -> curPlayPokers.first().getValue() > prePlayPokers.first().getValue()
                    PlayPokerType.BAN_ZI_PAO -> true
                    PlayPokerType.ZHA_DAN_SI_ZHANG -> true
                    PlayPokerType.HUO_JIAN_DAN -> true
                    PlayPokerType.GUN_TONG_PAO -> true
                    else -> false
                }
            }

            PlayPokerType.BAN_ZI_PAO -> {
                when (curPlayPokerType) {
                    PlayPokerType.BAN_ZI_PAO -> curPlayPokers.size > prePlayPokers.size ||
                            (curPlayPokers.size == prePlayPokers.size && curPlayPokers.first().getValue() > prePlayPokers.first()
                                .getValue())

                    PlayPokerType.ZHA_DAN_SI_ZHANG -> true
                    PlayPokerType.HUO_JIAN_DAN -> true
                    PlayPokerType.GUN_TONG_PAO -> true
                    else -> false
                }
            }

            PlayPokerType.ZHA_DAN_SI_ZHANG -> {
                when (curPlayPokerType) {
                    PlayPokerType.ZHA_DAN_SI_ZHANG -> curPlayPokers.first().getValue() > prePlayPokers.first().getValue()
                    PlayPokerType.HUO_JIAN_DAN -> true
                    PlayPokerType.GUN_TONG_PAO -> true
                    else -> false
                }
            }

            PlayPokerType.HUO_JIAN_DAN -> {
                when (curPlayPokerType) {
                    PlayPokerType.HUO_JIAN_DAN -> curPlayPokers.size > prePlayPokers.size ||
                            (curPlayPokers.size == prePlayPokers.size && curPlayPokers.first().getValue() > prePlayPokers.first()
                                .getValue())

                    PlayPokerType.GUN_TONG_PAO -> true
                    else -> false
                }
            }

            PlayPokerType.GUN_TONG_PAO -> {
                when (curPlayPokerType) {
                    PlayPokerType.GUN_TONG_PAO -> curPlayPokers.first().getValue() > prePlayPokers.first().getValue()
                    else -> false
                }
            }

            else -> false
        }
    }

    fun getPlayType(pokers: List<Poker>): PlayPokerType {
        return when {
            PlayPokerType.DAN_ZHANG.check(pokers) -> PlayPokerType.DAN_ZHANG
            PlayPokerType.DUI_ZI.check(pokers) -> PlayPokerType.DUI_ZI
            PlayPokerType.LIAN_ZI.check(pokers) -> PlayPokerType.LIAN_ZI
            PlayPokerType.ZHA_DAN_SAN_ZHANG.check(pokers) -> PlayPokerType.ZHA_DAN_SAN_ZHANG
            PlayPokerType.BAN_ZI_PAO.check(pokers) -> PlayPokerType.BAN_ZI_PAO
            PlayPokerType.ZHA_DAN_SI_ZHANG.check(pokers) -> PlayPokerType.ZHA_DAN_SI_ZHANG
            PlayPokerType.HUO_JIAN_DAN.check(pokers) -> PlayPokerType.HUO_JIAN_DAN
            PlayPokerType.GUN_TONG_PAO.check(pokers) -> PlayPokerType.GUN_TONG_PAO
            PlayPokerType.TIAN_BAO.check(pokers) -> PlayPokerType.TIAN_BAO
            else -> throw IllegalArgumentException("未知出牌类型")
        }
    }

    /**
     * 是否是炸弹
     */
    private fun PlayPokerType.isBoom(): Boolean {
        return this == PlayPokerType.ZHA_DAN_SAN_ZHANG ||
                this == PlayPokerType.BAN_ZI_PAO ||
                this == PlayPokerType.ZHA_DAN_SI_ZHANG ||
                this == PlayPokerType.HUO_JIAN_DAN ||
                this == PlayPokerType.GUN_TONG_PAO
    }
}

enum class PlayPokerType(val des: String) {
    DAN_ZHANG("单张") {
        override fun check(pokers: List<Poker>): Boolean {
            return pokers.size == 1
        }
    },
    DUI_ZI("对子") {
        override fun check(pokers: List<Poker>): Boolean {
            return pokers.size == 2 && pokers.isSameValue()
        }
    },
    LIAN_ZI("连子") {
        override fun check(pokers: List<Poker>): Boolean {
            return (pokers.size in 3..12) && pokers.isIncreasing(1) && pokers.last().getValue() != 15
        }
    },
    ZHA_DAN_SAN_ZHANG("三张炸弹") {
        override fun check(pokers: List<Poker>): Boolean {
            return pokers.size == 3 && pokers.isSameValue()
        }
    },
    BAN_ZI_PAO("板子炮") {
        override fun check(pokers: List<Poker>): Boolean {
            return (pokers.size == 6 || pokers.size == 8 || pokers.size == 10 || pokers.size == 12) &&
                    pokers.isIncreasing(2) && pokers.last().getValue() != 15
        }
    },
    ZHA_DAN_SI_ZHANG("四张炸弹") {
        override fun check(pokers: List<Poker>): Boolean {
            return pokers.size == 4 && pokers.isSameValue()
        }
    },
    HUO_JIAN_DAN("火箭弹") {
        override fun check(pokers: List<Poker>): Boolean {
            return (pokers.size == 9 || pokers.size == 12) &&
                    pokers.isIncreasing(3) && pokers.last().getValue() != 15
        }
    },
    GUN_TONG_PAO("滚筒炮") {
        override fun check(pokers: List<Poker>): Boolean {
            return (pokers.size == 12) && pokers.isIncreasing(4) && pokers.last().getValue() != 15
        }
    },
    TIAN_BAO("天爆") {
        override fun check(pokers: List<Poker>): Boolean {
            return pokers.size == 13 && pokers.isIncreasing(1)
        }
    };

    abstract fun check(pokers: List<Poker>): Boolean

    /**
     * 集合中的每张牌是否是一样的值
     */
    protected fun List<Poker>.isSameValue(): Boolean {
        val firstValue = this[0].getValue()
        for (i in 1 until this.size) {
            if (this[i].getValue() != firstValue) {
                return false
            }
        }
        return true
    }

    /**
     * 按照[chunkedSize]进行分组后，每组内的牌值是否是一致并且各组之间递增的
     */
    protected fun List<Poker>.isIncreasing(chunkedSize: Int): Boolean {
        var preValue = this[0].getValue()
        if (chunkedSize == 1) {
            for (i in 1 until this.size) {
                val curValue = this[i].getValue()
                if (curValue != preValue + 1) {
                    return false
                }
                preValue = curValue
            }
            return true
        } else {
            val chunked = chunked(chunkedSize)
            if (!chunked[0].isSameValue()) {
                return false
            }
            for (i in 1 until chunked.size) {
                if (!chunked[i].isSameValue()) {
                    return false
                }
                val curValue = chunked[i][0].getValue()
                if (curValue != preValue + 1) {
                    return false
                }
                preValue = curValue
            }
            return true
        }
    }
}
